/* 
 * File:   tcp_listen.cpp
 * Author: zhouxich
 * Description: 将服务器绑定到指定IP和端口的，并返回listen描述符，支持IPv6跟IPv4协议
 */
#include <iostream>
#include <sys/socket.h>
#include <sys/types.h> 
#include <netdb.h> 
#include <strings.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
using namespace std;

#define LISTENQ 1024
int
tcp_listen(const char *host, const char *serv, socklen_t *addrlen)
{
	int			listenfd, n;
	const int		on = 1;
	struct addrinfo	hints, *res, *ressave;

	bzero(&hints, sizeof(struct addrinfo));
	hints.ai_flags = AI_PASSIVE;					// 被动打开
	hints.ai_family = AF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;				// 数据流服务

	/*获取服务器地址信息*/
	if ( (n = getaddrinfo(host, serv, &hints, &res)) != 0)
		cout << "tcp_server error for " << host << ", " << serv << ": "
				<< gai_strerror(n) << endl;

	ressave = res;

	do {
		listenfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
		if (listenfd < 0)
			continue;		// 错误－尝试下一个
			

		setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
		if (bind(listenfd, res->ai_addr, res->ai_addrlen) == 0)
			break;			// 成功
//cout << strerror(errno) << endl;
		close(listenfd);	// 绑定出错，关闭socket并尝试下一个地址
	} while ( (res = res->ai_next) != NULL);

	if (res == NULL){		// 如果没有任何一个地址建立socket成功，出错
		cout << "tcp_server error for " << host << ", " << serv << endl;
		exit(1);
	}

	listen(listenfd, LISTENQ);

	if (addrlen)
		*addrlen = res->ai_addrlen;	// 返回协议地址的长度

	freeaddrinfo(ressave);

	return(listenfd);
}
/* end tcp_listen */

